home *** CD-ROM | disk | FTP | other *** search
- /* Virtual File System: FTP file system.
- Copyright (C) 1995 The Free Software Foundation
-
- Written by: 1995 Ching Hui
- 1995 Jakub Jelinek
- 1995 Miguel de Icaza
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
-
- /* FTPfs TODO:
-
- - make it more robust - all the connects etc. should handle EADDRINUSE and
- ERETRY (have I spelled these names correctly?)
- - make the user able to flush a connection - all the caches will get empty
- etc., (tarfs as well), we should give there a user selectable timeout
- and assign a key sequence.
- - use hash table instead of linklist to cache ftpfs directory.
- - complete rename operation.
- */
-
- #include <config.h>
- #include <stdio.h>
- #include <errno.h>
- #include <string.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <stdarg.h>
- #include <fcntl.h>
- #include <pwd.h>
- #include <time.h>
- #include <sys/types.h>
- #if defined(HAVE_UNISTD_H)
- #include <unistd.h>
- #endif
- #if IS_AIX
- # include <sys/select.h>
- #endif
- #include "../src/fs.h"
- #include "../src/mad.h"
- #include "../src/setup.h"
- #include "../src/tty.h" /* enable/disable interrupt key */
- #include <netdb.h> /* struct hostent */
- #include <sys/socket.h> /* AF_INET */
- #include <netinet/in.h> /* struct in_addr */
- #ifdef HAVE_SETSOCKOPT
- # include <netinet/ip.h> /* IP options */
- #endif
- #include <arpa/inet.h>
- #include <arpa/ftp.h>
- #include <arpa/telnet.h>
- #include <sys/time.h>
- #include <sys/param.h>
-
- #ifdef USE_TERMNET
- #include <termnet.h>
- #endif
-
- #include "../src/mem.h"
- #define WANT_PARSE_LS_LGA
- #include "vfs.h"
- #include "tcputil.h"
- #include "../src/util.h"
- #include "../src/dialog.h"
- #include "container.h"
- #include "ftpfs.h"
- #ifndef MAXHOSTNAMELEN
- # define MAXHOSTNAMELEN 64
- #endif
- #define UPLOAD_ZERO_LENGTH_FILE
-
- void print_vfs_message(char *, ...);
-
- static int ftpfserrno;
- static int code;
-
- /* Delay to retry a connection */
- int ftpfs_retry_seconds = 30;
-
- /* Use the ~/.netrc */
- int use_netrc = 1;
-
- extern char *home_dir;
-
- /* Anonymous setup */
- char *ftpfs_anonymous_passwd;
- int ftpfs_directory_timeout;
-
- /* Proxy host */
- char *ftpfs_proxy_host = 0;
-
- /* source routing host */
- extern int source_route;
-
- /* If set, then log dialog between server and client */
- static int ftpfs_debug_server_dialog = 0;
-
- /* Where we store the transactions */
- static FILE *ftpfs_logfile;
-
- /* If true, the directory cache is forced to reload */
- static int force_expiration = 0;
-
- struct linklist *ftpfs_connections_list;
-
- extern char *last_current_dir;
-
- /* command wait_flag: */
- #define NONE 0x00
- #define WAIT_REPLY 0x01
- #define WANT_STRING 0x02
- char reply_str [80];
-
- static char *ftpfs_get_current_directory(struct ftpfs_connection *bucket);
-
- /* Extract the hostname and username from the path */
- /* path is in the form: [user@]hostname:port/remote-dir, e.g.:
- * ftp://sunsite.unc.edu/pub/linux
- * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
- * ftp://tsx-11.mit.edu:8192/
- * ftp://joe@foo.edu:11321/private
- * If the user is empty, e.g. ftp://@roxanne/private, then your login name
- * is supplied.
- * */
-
- char *ftpfs_get_host_and_username (char *path, char **host, char **user, int *port, char **pass)
- {
- return get_host_and_username (path, host, user, port, 21, 1, pass);
- }
-
- static int
- get_line (int sock, char *buf, int buf_len)
- {
- int i, status;
- char c;
-
- for (i = 0; i < buf_len; i++, buf++) {
- if (read(sock, buf, sizeof(char)) <= 0)
- return 0;
- if (ftpfs_debug_server_dialog){
- fwrite (buf, 1, 1, ftpfs_logfile);
- fflush (ftpfs_logfile);
- }
- if (*buf == '\n') {
- *buf = 0;
- return 1;
- }
- }
- *buf = 0;
- while ((status = read(sock, &c, sizeof(c))) > 0){
- if (ftpfs_debug_server_dialog){
- fwrite (&c, 1, 1, ftpfs_logfile);
- fflush (ftpfs_logfile);
- }
- if (c == '\n')
- return 1;
- }
- return 0;
- }
-
- /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
- int getreply (int sock, char *string_buf, int string_len)
- {
- char answer[1024];
- int i;
-
- for (;;) {
- if (!get_line(sock, answer, sizeof(answer))) {
- if (string_buf)
- *string_buf = 0;
- code = 421;
- return 4;
- }
- switch(sscanf(answer, "%d", &code)) {
- case 0:
- if (string_buf) {
- strncpy(string_buf, answer, string_len - 1);
- *(string_buf + string_len - 1) = 0;
- }
- code = 500;
- return 5;
- case 1:
- if (answer[3] == '-') {
- while (1) {
- if (!get_line(sock, answer, sizeof(answer))) {
- if (string_buf)
- *string_buf = 0;
- code = 421;
- return 4;
- }
- if ((sscanf(answer, "%d", &i) > 0) &&
- (code == i) && (answer[3] == ' '))
- break;
- }
- }
- if (string_buf) {
- strncpy(string_buf, answer, string_len - 1);
- *(string_buf + string_len - 1) = 0;
- }
- return code / 100;
- }
- }
- }
-
- static void
- ftpentry_destructor(void *data)
- {
- struct ftpentry *fe = data;
-
- fe->count--;
- if (fe->count > 0)
- return;
- free(fe->name);
- if (fe->linkname)
- free(fe->linkname);
- if (fe->local_filename) {
- if (fe->local_is_temp) {
- if (!fe->local_stat.st_mtime)
- unlink(fe->local_filename);
- else {
- struct stat sb;
-
- if (stat (fe->local_filename, &sb) >=0 &&
- fe->local_stat.st_mtime == sb.st_mtime)
- unlink (fe->local_filename); /* Delete only if it hasn't changed */
- }
- }
- free(fe->local_filename);
- fe->local_filename = 0;
- }
- if (fe->remote_filename)
- free(fe->remote_filename);
- if (fe->l_stat)
- free(fe->l_stat);
- free(fe);
- }
-
- static void
- ftpfs_dir_destructor(void *data)
- {
- struct ftpfs_dir *fd = data;
-
- fd->count--;
- if (fd->count > 0)
- return;
- free(fd->remote_path);
- linklist_destroy(fd->file_list, ftpentry_destructor);
- free(fd);
- }
-
- static int command (struct ftpfs_connection *bucket, int wait_reply,
- char *fmt, ...)
- {
- va_list ap;
- char buf[2048]; /* FIXME: buffer exceed ?? */
- int n, status;
- int sock = qsock (bucket);
-
- va_start (ap, fmt);
- vsprintf (buf, fmt, ap);
- va_end (ap);
- n = strlen(buf);
- buf[n++] = '\r';
- buf[n++] = '\n';
- buf[n] = 0;
-
- if (ftpfs_debug_server_dialog){
- fwrite (buf, strlen (buf), 1, ftpfs_logfile);
- fflush (ftpfs_logfile);
- }
- got_sigpipe = 0;
- enable_interrupt_key();
- status = write(sock, buf, strlen(buf));
- disable_interrupt_key();
-
- if (status < 0){
- disable_interrupt_key();
- code = 421;
- if (errno == EPIPE)
- got_sigpipe = 1;
- return TRANSIENT;
- }
- if (wait_reply)
- return getreply (sock, (wait_reply & WANT_STRING) ? reply_str : NULL, sizeof (reply_str)-1);
- return COMPLETE;
- }
-
- static void
- ftpfs_connection_close (void *data)
- {
- struct ftpfs_connection *bucket = data;
-
- print_vfs_message ("ftpfs: Disconnecting from %s", qhost(bucket));
- command(bucket, WAIT_REPLY, "QUIT");
- close(qsock(bucket));
- }
-
- static void
- ftpfs_free_bucket (void *data)
- {
- struct ftpfs_connection *bucket = data;
-
- free(qhost(bucket));
- free(quser(bucket));
- if (qcdir(bucket))
- free(qcdir(bucket));
- if (qhome(bucket))
- free(qhome(bucket));
- if (qupdir(bucket))
- free(qupdir(bucket));
- linklist_destroy(qdcache(bucket), ftpfs_dir_destructor);
- free(bucket);
- }
-
- static void
- ftpfs_connection_destructor(void *data)
- {
- ftpfs_connection_close (data);
- ftpfs_free_bucket (data);
- }
-
- static int
- changetype (struct ftpfs_connection *bucket, int binary)
- {
- static int curr_binary = -1;
-
- if (binary != curr_binary) {
- if (command (bucket, WAIT_REPLY, "TYPE %c", binary ? 'I' : 'A') != COMPLETE) {
- ftpfserrno = EIO;
- return -1;
- }
- curr_binary = binary;
- }
- return binary;
- }
-
- inline void
- flush_all_directory(struct ftpfs_connection *bucket)
- {
- linklist_delete_all(qdcache(bucket), ftpfs_dir_destructor);
-
- }
-
- /* This routine logs the user in */
- static int
- login_server (struct ftpfs_connection *bucket, char *netrcpass)
- {
- char *pass;
- char *name; /* login user name */
-
- if (netrcpass)
- pass = strdup (netrcpass);
- else {
- if (!strcmp (quser(bucket), "anonymous") ||
- !strcmp (quser(bucket), "ftp"))
- pass = strdup(ftpfs_anonymous_passwd);
- else {
- char *p;
-
- p = copy_strings (" FTP: Password required for ", quser(bucket),
- " ", NULL);
- pass = input_dialog (p, "Password:", "");
- free (p);
- if (pass == NULL) {
- ftpfserrno = EPERM;
- return 0;
- }
- }
- }
-
- /* Proxy server accepts: username@host-we-want-to-connect*/
- if (qproxy (bucket)){
- /* qhost(bucket) has: "!hostname" */
- name = copy_strings (quser(bucket), "@", qhost(bucket)+1, 0);
- } else
- name = strdup (quser (bucket));
-
- if (getreply (qsock(bucket), NULL, 0) == COMPLETE) {
- print_vfs_message("ftpfs: sending login name");
- if ((code = command (bucket, WAIT_REPLY, "USER %s", name)) == CONTINUE) {
- print_vfs_message("ftpfs: sending user password");
- if (command (bucket, WAIT_REPLY, "PASS %s", pass) == COMPLETE)
- {
- print_vfs_message("ftpfs: logged in");
- wipe_password (pass);
- free (name);
- return 1;
- }
- } else {
- bucket->failed_on_login = 1;
-
- /* This matches the end of the code below, just to make it
- * obvious to the optimizer
- */
- wipe_password (pass);
- free (name);
- return 0;
- }
- }
- print_vfs_message ("ftpfs: Login incorrect for user %s ", quser(bucket));
- ftpfserrno = EPERM;
- wipe_password (pass);
- free (name);
- return 0;
- }
-
- #ifdef HAVE_SETSOCKOPT
- static void
- setup_source_route (int socket, int dest)
- {
- char buffer [20];
- char *ptr = buffer;
-
- if (!source_route)
- return;
- bzero (buffer, sizeof (buffer));
- *ptr++ = IPOPT_LSRR;
- *ptr++ = 3 + 8;
- *ptr++ = 4; /* pointer */
-
- /* First hop */
- bcopy ((char *) &source_route, ptr, sizeof (int));
- ptr += 4;
-
- /* Second hop (ie, final destination) */
- bcopy ((char *) &dest, ptr, sizeof (int));
- ptr += 4;
- while ((ptr - buffer) & 3)
- ptr++;
- if (setsockopt (socket, IPPROTO_IP, IP_OPTIONS,
- buffer, ptr - buffer) < 0)
- message (1, " Error ", " Could not set source routing (%s)", unix_error_string (errno));
- }
- #else
- #define setup_source_route(x,y)
- #endif
-
- static int
- ftpfs_open_socket(struct ftpfs_connection *bucket)
- {
- struct sockaddr_in server_address;
- struct hostent *hp;
- int my_socket;
- char *host;
-
- /* Use a proxy host? */
- host = qhost(bucket);
-
- if (!host || !*host){
- print_vfs_message ("ftpfs: Invalid host name.");
- return -1;
- }
-
- /* Hosts to connect to that start with a ! should use proxy */
- if (*host == '!' && ftpfs_proxy_host){
- bucket->use_proxy = 1;
- host = ftpfs_proxy_host;
- }
-
- /* Get host address */
- bzero ((char *) &server_address, sizeof (server_address));
- server_address.sin_family = AF_INET;
- server_address.sin_addr.s_addr = inet_addr (host);
- if (server_address.sin_addr.s_addr != -1)
- server_address.sin_family = AF_INET;
- else {
- hp = gethostbyname(host);
- if (hp == NULL){
- print_vfs_message("ftpfs: Invalid host address.");
- ftpfserrno = EINVAL;
- return -1;
- }
- server_address.sin_family = hp->h_addrtype;
- bcopy ((char *) hp->h_addr, (char *) &server_address.sin_addr,
- hp->h_length);
- }
-
- server_address.sin_port = htons (qport(bucket));
-
- /* Connect */
- if ((my_socket = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
- ftpfserrno = errno;
- return -1;
- }
- setup_source_route (my_socket, server_address.sin_addr.s_addr);
-
- print_vfs_message("ftpfs: making connection to %s", host);
-
- enable_interrupt_key(); /* clear the interrupt flag */
-
- if (connect (my_socket, (struct sockaddr *) &server_address,
- sizeof (server_address)) < 0){
- ftpfserrno = errno;
- if (errno == EINTR && got_interrupt())
- print_vfs_message("ftpfs: connection interrupted by user");
- else
- print_vfs_message("ftpfs: connection to server failed: %s",
- unix_error_string(errno));
- disable_interrupt_key();
- close (my_socket);
- return -1;
- }
- disable_interrupt_key();
- return my_socket;
- }
-
- static struct ftpfs_connection *
- open_command_connection (char *host, char *user, int port, char *netrcpass)
- {
- struct ftpfs_connection *bucket;
- int retry_seconds, count_down;
-
- bucket = xmalloc(sizeof(struct ftpfs_connection),
- "struct ftpfs_connection");
-
- if (bucket == NULL) {
- ftpfserrno = ENOMEM;
- return NULL;
- }
- qhost(bucket) = host;
- quser(bucket) = user;
- qcdir(bucket) = NULL;
- qport(bucket) = port;
- qlock(bucket) = 0;
- qhome(bucket) = NULL;
- qproxy(bucket)= 0;
- qupdir(bucket)= 0;
- qdcache(bucket)=0;
- bucket->lock = 0;
- bucket->use_proxy = 0;
- bucket->use_source_route = source_route;
- retry_seconds = 0;
-
- if ((qdcache(bucket) = linklist_init()) == NULL) {
- ftpfserrno = ENOMEM;
- free(host);
- free(user);
- free(bucket);
- return NULL;
- }
-
- do {
- bucket->failed_on_login = 0;
-
- qsock(bucket) = ftpfs_open_socket(bucket);
- if (qsock(bucket) == -1) {
- ftpfs_free_bucket (bucket);
- return NULL;
- }
-
- if (!login_server(bucket, netrcpass)) {
- if (!bucket->failed_on_login){
- ftpfs_connection_destructor (bucket);
- return NULL;
- }
- if (ftpfs_retry_seconds){
- retry_seconds = ftpfs_retry_seconds;
- enable_interrupt_key ();
- for (count_down = retry_seconds; count_down; count_down--){
- print_vfs_message ("Waiting to retry... %d\n", count_down);
- sleep (1);
- if (got_interrupt ()){
- retry_seconds = 0;
- break;
- }
- }
- disable_interrupt_key ();
- }
- }
- } while (retry_seconds);
-
- qhome(bucket) = ftpfs_get_current_directory (bucket);
- if (!qhome(bucket))
- qhome(bucket) = strdup ("/");
- if (last_current_dir) {
- if (last_current_dir [strlen (last_current_dir) - 1] == '/')
- qupdir(bucket) = strdup (last_current_dir);
- else
- qupdir(bucket) = copy_strings (last_current_dir, "/", NULL);
- } else
- qupdir(bucket) = strdup ("/");
- return bucket;
- }
-
- static int
- is_connection_closed(struct ftpfs_connection *bucket)
- {
- struct fd_set rset;
- struct timeval t;
-
- if (got_sigpipe)
- return 1;
- t.tv_sec = 0;
- t.tv_usec = 0;
- FD_ZERO(&rset);
- FD_SET(qsock(bucket), &rset);
- while (1) {
- if (select(qsock(bucket) + 1, &rset, NULL, NULL, &t) < 0)
- if (errno != EINTR)
- return 1;
- return 0;
- #if 0
- if (FD_ISSET(qsock(bucket), &rset)) {
- n = read(qsock(bucket), &read_ahead, sizeof(read_ahead));
- if (n <= 0)
- return 1;
- } else
- #endif
- }
- }
-
- /* This routine keeps track of open connections */
- /* Returns a connected socket to host */
- static struct ftpfs_connection *
- ftpfs_open_link (char *host, char *user, int port, char *netrcpass)
- {
- int sock;
- struct ftpfs_connection *bucket;
- struct linklist *lptr;
-
- for (lptr = ftpfs_connections_list->next;
- lptr != ftpfs_connections_list; lptr = lptr->next) {
- bucket = lptr->data;
- if ((strcmp (host, qhost(bucket)) == 0) &&
- (strcmp (user, quser(bucket)) == 0) &&
- (port == qport(bucket))) {
- free(host);
- free(user);
-
- /* check the connection is closed or not, just hack */
- if (is_connection_closed(bucket)) {
- flush_all_directory(bucket);
- sock = ftpfs_open_socket(bucket);
- if (sock != -1) {
- close(qsock(bucket));
- qsock(bucket) = sock;
- if (login_server(bucket, netrcpass))
- return bucket;
- }
-
- /* connection refused */
- lptr->prev->next = lptr->next;
- lptr->next->prev = lptr->prev;
- ftpfs_connection_destructor(bucket);
- return NULL;
- }
- return bucket;
- }
- }
- bucket = open_command_connection(host, user, port, netrcpass);
- if (bucket == NULL)
- return NULL;
- if (!linklist_insert(ftpfs_connections_list, bucket)) {
- ftpfs_connection_destructor(bucket);
- return NULL;
- }
- return bucket;
- }
-
- /* The returned directory should always contain a trailing slash */
- static char *ftpfs_get_current_directory(struct ftpfs_connection *bucket)
- {
- char buf[4096], *bufp, *bufq;
-
- if (command(bucket, NONE, "PWD") != COMPLETE)
- return NULL;
- if (getreply(qsock(bucket), buf, sizeof(buf)) == COMPLETE) {
- bufp = NULL;
- for (bufq = buf; *bufq; bufq++)
- if (*bufq == '"') {
- if (!bufp) {
- bufp = bufq + 1;
- } else {
- *bufq = 0;
- if (*bufp) {
- if (*(bufq - 1) != '/') {
- *bufq++ = '/';
- *bufq = 0;
- }
- return strdup (bufp);
- } else {
- ftpfserrno = EIO;
- return NULL;
- }
- }
- }
- }
- ftpfserrno = EIO;
- return NULL;
- }
-
- void ftpfs_fill_names (void (*func)(char *))
- {
- struct linklist *lptr;
- char *path_name;
- struct ftpfs_connection *bucket;
-
- if (!ftpfs_connections_list)
- return;
- lptr = ftpfs_connections_list;
- do {
- if ((bucket = lptr->data) != 0){
-
- path_name = copy_strings ("ftp://", quser (bucket),
- "@", qhost (bucket),
- qcdir(bucket), 0);
- (*func)(path_name);
- free (path_name);
- }
- lptr = lptr->next;
- } while (lptr != ftpfs_connections_list);
- }
-
-
- /* Setup Passive ftp connection, we use it for source routed connections */
- static int
- setup_passive (int my_socket, struct ftpfs_connection *bucket, struct sockaddr_in *sa)
- {
- int xa, xb, xc, xd, xe, xf;
- char n [6];
- char *c = reply_str;
-
- if (command (bucket, WAIT_REPLY | WANT_STRING, "PASV") != COMPLETE)
- return 0;
-
- /* Parse remote parameters */
- for (c = reply_str + 4; (*c) && (!isdigit (*c)); c++)
- ;
- if (!*c)
- return 0;
- if (!isdigit (*c))
- return 0;
- if (sscanf (c, "%d,%d,%d,%d,%d,%d", &xa, &xb, &xc, &xd, &xe, &xf) != 6)
- return 0;
- n [0] = (unsigned char) xa;
- n [1] = (unsigned char) xb;
- n [2] = (unsigned char) xc;
- n [3] = (unsigned char) xd;
- n [4] = (unsigned char) xe;
- n [5] = (unsigned char) xf;
-
- bcopy ((void *)n, &(sa->sin_addr.s_addr), 4);
- bcopy ((void *)&n[4], &(sa->sin_port), 2);
- setup_source_route (my_socket, sa->sin_addr.s_addr);
- if (connect (my_socket, (struct sockaddr *) sa, sizeof (struct sockaddr_in)) < 0)
- return 0;
- return 1;
- }
-
- static int
- initconn (struct ftpfs_connection *bucket)
- {
- struct sockaddr_in data_addr;
- int data, len = sizeof(data_addr);
- struct protoent *pe;
-
- getsockname(qsock(bucket), (struct sockaddr *) &data_addr, &len);
- data_addr.sin_port = 0;
-
- pe = getprotobyname("tcp");
- if (pe == NULL) {
- ftpfserrno = EIO;
- return -1;
- }
- data = socket (AF_INET, SOCK_STREAM, pe->p_proto);
- if (data < 0) {
- ftpfserrno = EIO;
- return -1;
- }
-
- if (bucket->use_source_route){
- int c;
-
- if ((c = setup_passive (data, bucket, &data_addr)))
- return data;
- print_vfs_message("ftpfs: could not setup passive mode for source routing");
- bucket->use_source_route = 0;
- }
- /* If passive setup fails, fallback to active connections */
- /* Active FTP connection */
- if (bind (data, (struct sockaddr *)&data_addr, len) < 0)
- goto error_return;
- getsockname(data, (struct sockaddr *) &data_addr, &len);
- if (listen (data, 1) < 0)
- goto error_return;
- {
- unsigned char *a = (unsigned char *)&data_addr.sin_addr;
- unsigned char *p = (unsigned char *)&data_addr.sin_port;
-
- if (command (bucket, WAIT_REPLY, "PORT %d,%d,%d,%d,%d,%d", a[0], a[1],
- a[2], a[3], p[0], p[1]) != COMPLETE)
- goto error_return;
- }
- return data;
- error_return:
- close(data);
- ftpfserrno = EIO;
- return -1;
- }
-
- static int
- open_data_connection (struct ftpfs_connection *bucket, char *cmd, char *remote,
- int isbinary)
- {
- struct sockaddr_in from;
- int s, j, data, fromlen = sizeof(from);
-
- if ((s = initconn (bucket)) == -1)
- return -1;
- if (changetype (bucket, isbinary) == -1)
- return -1;
- if (remote)
- j = command (bucket, WAIT_REPLY, "%s %s", cmd, remote);
- else
- j = command (bucket, WAIT_REPLY, "%s", cmd);
- if (j != PRELIM) {
- ftpfserrno = EPERM;
- return -1;
- }
- enable_interrupt_key();
- if (bucket->use_source_route)
- data = s;
- else {
- data = accept (s, (struct sockaddr *)&from, &fromlen);
- if (data < 0) {
- ftpfserrno = errno;
- close(s);
- return -1;
- }
- close(s);
- }
- disable_interrupt_key();
- return data;
- }
-
- static char *ftpfs_get_path (struct ftpfs_connection **bucket, char *path)
- {
- char *user, *host, *remote_path, *pass;
- int port;
-
- /* An absolute path name, try to determine connection socket */
- if (strncmp (path, "ftp://", 6) == 0){
- path += 6;
-
- if (!(remote_path = ftpfs_get_host_and_username (path, &host, &user,
- &port, &pass))) {
- ftpfserrno = ENOENT;
- return NULL;
- }
- if ((*bucket = ftpfs_open_link (host, user, port, pass)) == NULL) {
- free (remote_path);
- if (pass)
- wipe_password (pass);
- return NULL;
- }
- if (pass)
- wipe_password (pass);
- return remote_path;
- }
- /* never get here !!! */
- message(1, " Error ", " Oops, you just hit a bug in the code ");
- return NULL;
- }
-
- static void
- ftpfs_abort (struct ftpfs_connection *bucket, int dsock)
- {
- static unsigned char ipbuf[3] = { IAC, IP, IAC };
- fd_set mask;
- char buf[1024];
-
- print_vfs_message("ftpfs: aborting transfer.");
- if (send(qsock(bucket), ipbuf, sizeof(ipbuf), MSG_OOB) != sizeof(ipbuf)) {
- print_vfs_message("ftpfs: abort error: %s", unix_error_string(errno));
- return;
- }
-
- if (command(bucket, NONE, "%cABOR", DM) != COMPLETE){
- print_vfs_message ("ftpfs: abort failed");
- return;
- }
- if (dsock != -1) {
- FD_ZERO(&mask);
- FD_SET(dsock, &mask);
- if (select(dsock + 1, &mask, NULL, NULL, NULL) > 0)
- while (read(dsock, buf, sizeof(buf)) > 0);
- }
- if ((getreply(qsock(bucket), NULL, 0) == TRANSIENT) && (code == 426))
- getreply(qsock(bucket), NULL, 0);
- }
-
- static void
- resolve_symlink(struct ftpfs_connection *bucket, struct ftpfs_dir *dir)
- {
- char buffer[2048] = "", *filename;
- int sock;
- FILE *fp;
- struct stat s;
- struct linklist *flist;
- struct ftpentry *fe;
-
- print_vfs_message("Resolving symlink...");
-
- sock = open_data_connection (bucket, "LIST -lLa",
- dir->remote_path, 0);
- if (sock == -1) {
- print_vfs_message("ftpfs: couldn't resolve symlink");
- return;
- }
-
- fp = fdopen(sock, "r");
- if (fp == NULL) {
- close(sock);
- print_vfs_message("ftpfs: couldn't resolve symlink");
- return;
- }
- enable_interrupt_key();
- flist = dir->file_list->next;
- while (1) {
- do {
- if (flist == dir->file_list)
- goto done;
- fe = flist->data;
- flist = flist->next;
- } while (!S_ISLNK(fe->s.st_mode));
- while (1) {
- if (parse_ls_lga (buffer, &s, &filename, NULL)) {
- int r = strcmp(fe->name, filename);
- free(filename);
- if (r == 0) {
- fe->l_stat = xmalloc(sizeof(struct stat),
- "resolve_symlink: struct stat");
- if (fe->l_stat == NULL)
- goto done;
- *fe->l_stat = s;
- break;
- }
- if (r < 0)
- break;
- }
- if (fgets (buffer, sizeof (buffer), fp) == NULL)
- goto done;
- }
- }
- done:
- while (fgets(buffer, sizeof(buffer), fp) != NULL);
- disable_interrupt_key();
- fclose(fp);
- getreply(qsock(bucket), NULL, 0);
- }
-
-
- static struct ftpfs_dir *
- retrieve_dir(struct ftpfs_connection *bucket, char *remote_path)
- {
- FILE *fp;
- int sock, has_symlinks;
- struct linklist *file_list, *p;
- struct ftpentry *fe;
- char buffer[8192];
- struct ftpfs_dir *dcache;
- int got_intr = 0;
-
- for (p = qdcache(bucket)->next;p != qdcache(bucket);
- p = p->next) {
- dcache = p->data;
- if (strcmp(dcache->remote_path, remote_path) == 0) {
- struct timeval tim;
-
- gettimeofday(&tim, NULL);
- if ((tim.tv_sec < dcache->timestamp.tv_sec) && !force_expiration)
- return dcache;
- else {
- force_expiration = 0;
- p->next->prev = p->prev;
- p->prev->next = p->next;
- ftpfs_dir_destructor(dcache);
- break;
- }
- }
- }
-
- has_symlinks = 0;
- print_vfs_message("ftpfs: Reading FTP directory...");
- if (command(bucket, WAIT_REPLY, "CWD %s", remote_path) != COMPLETE) {
- ftpfserrno = ENOENT;
- print_vfs_message("ftpfs: CWD failed.");
- return NULL;
- }
-
- file_list = linklist_init();
- if (file_list == NULL) {
- ftpfserrno = ENOMEM;
- print_vfs_message("ftpfs: couldn't get a file listing");
- return NULL;
- }
- dcache = xmalloc(sizeof(struct ftpfs_dir),
- "struct ftpfs_dir");
- if (dcache == NULL) {
- ftpfserrno = ENOMEM;
- linklist_destroy(file_list, NULL);
- print_vfs_message("ftpfs: FAIL");
- return NULL;
- }
- gettimeofday(&dcache->timestamp, NULL);
- dcache->timestamp.tv_sec += ftpfs_directory_timeout;
- dcache->file_list = file_list;
- dcache->remote_path = strdup(remote_path);
- dcache->count = 1;
- sock = open_data_connection (bucket, "LIST -la", ".", 0);
- if (sock == -1)
- goto error_3;
- fp = fdopen(sock, "r");
- if (fp == NULL) {
- close(sock);
- goto error_2;
- }
-
- /* Clear the interrupt flag */
- enable_interrupt_key ();
-
- errno = 0;
- while (fgets (buffer, sizeof (buffer), fp) != NULL) {
-
- if (got_intr = got_interrupt ())
- break;
-
- fe = xmalloc(sizeof(struct ftpentry), "struct ftpentry");
- fe->freshly_created = 0;
- if (fe == NULL) {
- ftpfserrno = ENOMEM;
- goto error_1;
- }
- if (parse_ls_lga (buffer, &fe->s, &fe->name, &fe->linkname)) {
- fe->count = 1;
- fe->local_filename = fe->remote_filename = NULL;
- fe->l_stat = NULL;
- fe->bucket = bucket;
- if (S_ISLNK(fe->s.st_mode))
- has_symlinks = 1;
- if (!linklist_insert(file_list, fe)) {
- free(fe);
- ftpfserrno = ENOMEM;
- goto error_1;
- }
- }
- else
- free(fe);
- }
- if (got_intr){
- disable_interrupt_key();
- print_vfs_message("ftpfs: reading FTP directory interrupt by user");
- ftpfs_abort(bucket, fileno(fp));
- fclose(fp);
- ftpfserrno = EINTR;
- goto error_3;
- }
- fclose(fp);
- disable_interrupt_key();
- if (getreply (qsock (bucket), NULL, 0) != COMPLETE) {
- ftpfserrno = EIO;
- goto error_3;
- }
- if (file_list->next == file_list) {
- ftpfserrno = EACCES;
- goto error_3;
- }
- if (!linklist_insert(qdcache(bucket), dcache)) {
- ftpfserrno = ENOMEM;
- goto error_3;
- }
- if (has_symlinks)
- resolve_symlink(bucket, dcache);
- print_vfs_message("ftpfs: got listing");
- return dcache;
- error_1:
- disable_interrupt_key();
- fclose(fp);
- error_2:
- getreply(qsock(bucket), NULL, 0);
- error_3:
- free(dcache->remote_path);
- free(dcache);
- linklist_destroy(file_list, ftpentry_destructor);
- print_vfs_message("ftpfs: failed");
- return NULL;
- }
-
- static int
- store_file(struct ftpentry *fe)
- {
- int local_handle, sock, n, total;
- #ifdef HAVE_STRUCT_LINGER
- struct linger li;
- #else
- int flag_one = 1;
- #endif
- char buffer[8192];
- struct stat s;
-
- local_handle = open(fe->local_filename, O_RDONLY);
- unlink (fe->local_filename);
- if (local_handle == -1) {
- ftpfserrno = EIO;
- return 0;
- }
- fstat(local_handle, &s);
- sock = open_data_connection(fe->bucket, "STOR", fe->remote_filename, 1);
- if (sock < 0) {
- close(local_handle);
- return 0;
- }
- #ifdef HAVE_STRUCT_LINGER
- li.l_onoff = 1;
- li.l_linger = 120;
- setsockopt(sock, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof(li));
- #else
- setsockopt(sock, SOL_SOCKET, SO_LINGER, &flag_one, sizeof (flag_one));
- #endif
- total = 0;
-
- enable_interrupt_key();
- while (1) {
- while ((n = read(local_handle, buffer, sizeof(buffer))) < 0) {
- if (errno == EINTR) {
- if (got_interrupt()) {
- ftpfserrno = EINTR;
- goto error_return;
- }
- else
- continue;
- }
- ftpfserrno = errno;
- goto error_return;
- }
- if (n == 0)
- break;
- while (write(sock, buffer, n) < 0) {
- if (errno == EINTR) {
- if (got_interrupt()) {
- ftpfserrno = EINTR;
- goto error_return;
- }
- else
- continue;
- }
- ftpfserrno = errno;
- goto error_return;
- }
- total += n;
- print_vfs_message("ftpfs: storing file %d (%d)",
- total, s.st_size);
- }
- disable_interrupt_key();
- close(sock);
- close(local_handle);
- if (getreply (qsock (fe->bucket), NULL, 0) != COMPLETE) {
- ftpfserrno = EIO;
- return 0;
- }
- return 1;
- error_return:
- disable_interrupt_key();
- close(sock);
- close(local_handle);
- getreply(qsock(fe->bucket), NULL, 0);
- return 0;
- }
-
- /* These variables are for the _ctl routine */
- static char *localname = NULL;
- static struct ftpentry *remoteent;
- static int remotetotal = 0;
- static int transfer_started = 0;
- static char *remotebuffer;
- static int isremotecopy = 0;
- static int remotelocal_handle, remotesock, remoten, remotestat_size;
-
- int retrieve_file_start(struct ftpentry *fe)
- {
- if (fe->local_filename == NULL) {
- ftpfserrno = ENOMEM;
- return 0;
- }
- remotesock = open_data_connection(fe->bucket, "RETR", fe->remote_filename, 1);
- if (remotesock == -1) {
- ftpfserrno = EACCES;
- free (fe->local_filename);
- fe->local_filename = NULL;
- return 0;
- }
- remotetotal = 0;
- remoteent = fe;
- return 1;
- }
-
- int retrieve_file_start2(struct ftpentry *fe)
- {
- remotelocal_handle = open(fe->local_filename, O_RDWR | O_CREAT | O_TRUNC, 0600);
- if (remotelocal_handle == -1) {
- ftpfserrno = EIO;
- free(fe->local_filename);
- fe->local_filename = NULL;
- fe->local_is_temp = 1;
- close(remotesock);
- return 0;
- }
- remotestat_size = fe->s.st_size;
- remotebuffer = xmalloc (8192, "");
- return 1;
- }
-
- void ftpfs_flushdir ()
- {
- force_expiration = 1;
- }
-
- int ftpfs_ctl (void *data, int ctlop, int arg)
- {
- int n = 0;
-
- switch (ctlop) {
- case MCCTL_ISREMOTECOPY:
- return isremotecopy;
-
- case MCCTL_REMOTECOPYCHUNK:
- if (!transfer_started)
- if (!retrieve_file_start2 (remoteent)){
- return MCERR_TARGETOPEN;
- } else
- transfer_started = 1;
-
- enable_interrupt_key ();
- if (!remoten) {
- fd_set set;
- int v;
-
- FD_ZERO (&set);
- FD_SET (remotesock, &set);
- FD_SET (0, &set);
- v = select (remotesock+1, &set, 0, 0, NULL);
-
- if (((v < 0) && (errno == EINTR)) || (FD_ISSET (0, &set))){
- disable_interrupt_key ();
- return MCERR_DATA_ON_STDIN;
- }
-
- if ((n = read(remotesock, remotebuffer, 8192)) < 0){
- disable_interrupt_key ();
- if (errno == EINTR)
- return MCERR_DATA_ON_STDIN;
- else
- return MCERR_READ;
- }
- if (n == 0) {
- if (getreply (qsock (remoteent->bucket), NULL, 0) != COMPLETE) {
- ftpfserrno = EIO;
- }
- close(remotelocal_handle);
- close(remotesock);
- if (localname){
- free (localname);
- localname = NULL;
- }
- disable_interrupt_key ();
- transfer_started = 0;
- return MCERR_FINISH;
- }
- disable_interrupt_key ();
- remotetotal += n;
- remoten = n;
- } else
- n = remoten;
- if (write(remotelocal_handle, remotebuffer, remoten) < 0)
- return MCERR_WRITE;
- remoten = 0;
- return n;
-
- /* We get this message if the transfer was aborted */
- case MCCTL_FINISHREMOTE:
- if (localname) {
- free (localname);
- localname = NULL;
- }
- if (!arg) { /* OK */
- if (stat (remoteent->local_filename, &remoteent->local_stat) < 0)
- remoteent->local_stat.st_mtime = 0;
- } else
- remoteent->local_stat.st_mtime = 0;
- transfer_started = 0;
- ftpfs_abort (remoteent->bucket, remotesock);
- ftpfserrno = EINTR;
- return 0;
- }
- return 0;
- }
-
- int ftpfs_setctl (char *path, int ctlop, char *arg)
- {
- switch (ctlop) {
- case MCCTL_SETREMOTECOPY: if (localname) free (localname);
- localname = strdup (vfs_canon (arg));
- return 1;
- default:
- return 0;
- }
- }
-
- int retrieve_file(struct ftpentry *fe)
- {
- int total;
- char buffer[8192];
- int local_handle, sock, n;
-
- if (fe->local_filename)
- return 1;
- fe->local_stat.st_mtime = 0;
- fe->local_filename = strdup(tmpnam(NULL));
- fe->local_is_temp = 1;
- if (fe->local_filename == NULL) {
- ftpfserrno = ENOMEM;
- return 0;
- }
- local_handle = open(fe->local_filename, O_RDWR | O_CREAT | O_TRUNC, 0600);
- if (local_handle == -1) {
- ftpfserrno = EIO;
- free(fe->local_filename);
- fe->local_filename = NULL;
- return 0;
- }
- sock = open_data_connection(fe->bucket, "RETR", fe->remote_filename, 1);
- if (sock == -1) {
- ftpfserrno = EACCES;
- goto error_3;
- }
-
- /* Clear the interrupt status */
- enable_interrupt_key ();
- total = 0;
- while (1) {
- int stat_size = fe->s.st_size;
- while ((n = read(sock, buffer, sizeof(buffer))) < 0) {
- if (errno == EINTR) {
- if (got_interrupt ()) {
- disable_interrupt_key();
- ftpfs_abort(fe->bucket, sock);
- ftpfserrno = EINTR;
- goto error_2;
- }
- else
- continue;
- }
- ftpfserrno = errno;
- disable_interrupt_key();
- goto error_1;
- }
- if (n == 0)
- break;
- total += n;
- if (stat_size == 0)
- print_vfs_message ("ftpfs: Getting file: %ld bytes transfered",
- total);
- else
- print_vfs_message ("ftpfs: Getting file: %3d%% (%ld bytes transfered)",
- total*100/stat_size, total);
- while (write(local_handle, buffer, n) < 0) {
- if (errno == EINTR) {
- if (got_interrupt()) {
- ftpfs_abort(fe->bucket, sock);
- ftpfserrno = EINTR;
- goto error_2;
- }
- else
- continue;
- }
- ftpfserrno = errno;
- goto error_1;
- }
- }
- close(local_handle);
- close(sock);
- if (getreply (qsock (fe->bucket), NULL, 0) != COMPLETE) {
- ftpfserrno = EIO;
- goto error_2;
- }
- if (stat (fe->local_filename, &fe->local_stat) < 0)
- fe->local_stat.st_mtime = 0;
- return 1;
- error_1:
- getreply(qsock(fe->bucket), NULL, 0);
- error_2:
- close(sock);
- error_3:
- disable_interrupt_key ();
- close(local_handle);
- unlink(fe->local_filename);
- free(fe->local_filename);
- fe->local_filename = NULL;
- return 0;
- }
-
- static struct ftpentry *
- _get_file_entry(struct ftpfs_connection *bucket, char *file_name,
- int op, int flags)
- {
- char *p, q;
- struct ftpentry *ent;
- struct linklist *file_list, *lptr;
- struct ftpfs_dir *dcache;
- struct stat sb;
-
- p = strrchr(file_name, '/');
- q = *p;
- *p = '\0';
- dcache = retrieve_dir(bucket, *file_name ? file_name : "/");
- if (dcache == NULL)
- return NULL;
- file_list = dcache->file_list;
- *p++ = q;
- if (!*p)
- p = ".";
- for (lptr = file_list->next; lptr != file_list; lptr = lptr->next) {
- ent = lptr->data;
- if (strcmp(p, ent->name) == 0) {
- if (S_ISLNK(ent->s.st_mode) && (op & FTPFS_RESOLVE_SYMLINK)) {
- if (ent->l_stat == NULL) {
- ftpfserrno = ENOENT;
- return NULL;
- }
- if (S_ISLNK(ent->l_stat->st_mode)) {
- ftpfserrno = ELOOP;
- return NULL;
- }
- }
- if (ent && (op & FTPFS_OPEN)) {
- mode_t fmode;
-
- fmode = S_ISLNK(ent->s.st_mode)
- ? ent->l_stat->st_mode
- : ent->s.st_mode;
- if (S_ISDIR(fmode)) {
- ftpfserrno = EISDIR;
- return NULL;
- }
- if (!S_ISREG(fmode)) {
- ftpfserrno = EPERM;
- return NULL;
- }
- if ((flags & O_EXCL) && (flags & O_CREAT)) {
- ftpfserrno = EEXIST;
- return NULL;
- }
- if (ent->remote_filename == NULL) {
- ent->remote_filename = strdup(file_name);
- if (ent->remote_filename == NULL) {
- ftpfserrno = ENOMEM;
- return NULL;
- }
- }
- if (ent->local_filename == NULL ||
- !ent->local_stat.st_mtime ||
- stat (ent->local_filename, &sb) < 0 ||
- sb.st_mtime != ent->local_stat.st_mtime) {
- int handle;
-
- if (ent->local_filename){
- free (ent->local_filename);
- ent->local_filename = 0;
- }
- if (flags & O_TRUNC) {
- ent->local_filename = strdup(tmpnam(NULL));
- if (ent->local_filename == NULL) {
- ftpfserrno = ENOMEM;
- return NULL;
- }
- handle = open(ent->local_filename, O_CREAT | O_TRUNC |
- O_RDWR, 0600);
- if (handle < 0) {
- ftpfserrno = EIO;
- return NULL;
- }
- close(handle);
- if (stat (ent->local_filename, &ent->local_stat) < 0)
- ent->local_stat.st_mtime = 0;
- }
- else {
- if (localname != NULL) {
- isremotecopy = 1;
- ent->local_is_temp = 0;
- ent->local_stat.st_mtime = 0;
- ent->local_filename = strdup (localname);
- if (!retrieve_file_start (ent)) {
- isremotecopy = 0;
- return NULL;
- }
- return ent;
- }
- if (!retrieve_file(ent))
- return NULL;
- }
- }
- else if (flags & O_TRUNC) {
- truncate(ent->local_filename, 0);
- }
- }
- return ent;
- }
- }
- if ((op & FTPFS_OPEN) && (flags & O_CREAT)) {
- int handle;
-
- ent = xmalloc(sizeof(struct ftpentry), "struct ftpentry");
- ent->freshly_created = 0;
- if (ent == NULL) {
- ftpfserrno = ENOMEM;
- return NULL;
- }
- ent->count = 1;
- ent->linkname = NULL;
- ent->l_stat = NULL;
- ent->bucket = bucket;
- ent->name = strdup(p);
- ent->remote_filename = strdup(file_name);
- ent->local_filename = strdup(tmpnam(NULL));
- if (!ent->name && !ent->remote_filename && !ent->local_filename) {
- ftpentry_destructor(ent);
- ftpfserrno = ENOMEM;
- return NULL;
- }
- handle = creat(ent->local_filename, 0700);
- if (handle == -1) {
- ftpfserrno = EIO;
- ftpentry_destructor(ent);
- return NULL;
- }
- fstat(handle, &ent->s);
- close(handle);
- #if 0
- /* This is very wrong - like this a zero length file will be always created
- and usually preclude uploading anything more desirable */
- #if defined(UPLOAD_ZERO_LENGTH_FILE)
- if (!store_file(ent)) {
- ftpentry_destructor(ent);
- return NULL;
- }
- #endif
- #endif
- if (!linklist_insert(file_list, ent)) {
- ftpfserrno = ENOMEM;
- ftpentry_destructor(ent);
- return NULL;
- }
- ent->freshly_created = 1;
- return ent;
- }
- else {
- ftpfserrno = ENOENT;
- return NULL;
- }
- }
-
- static struct ftpentry *
- get_file_entry(char *path, int op, int flags)
- {
- struct ftpfs_connection *bucket;
- struct ftpentry *fe;
- char *remote_path;
-
- if (!(remote_path = ftpfs_get_path (&bucket, path)))
- return NULL;
- isremotecopy = 0;
- fe = _get_file_entry(bucket, remote_path, op,
- flags);
- free(remote_path);
- if (op & FTPFS_FREE_RESOURCE)
- vfs_add_noncurrent_stamps (&ftpfs_vfs_ops, (vfsid) bucket, NULL);
- return fe;
- }
-
- #define OPT_FLUSH 1
- #define OPT_IGNORE_ERROR 2
-
- static int ftpfs_normal_flush = 1;
-
- void ftpfs_hint_reread(int reread)
- {
- if (reread)
- ftpfs_normal_flush++;
- else
- ftpfs_normal_flush--;
- }
-
- static int
- send_ftp_command(char *filename, char *cmd, int flags)
- {
- char *remote_path;
- struct ftpfs_connection *bucket;
- int r;
- int flush_directory_cache = (flags & OPT_FLUSH) && (ftpfs_normal_flush > 0);
-
- if (!(remote_path = ftpfs_get_path(&bucket, filename)))
- return -1;
- r = command (bucket, WAIT_REPLY, cmd, remote_path);
- free(remote_path);
- vfs_add_noncurrent_stamps (&ftpfs_vfs_ops, (vfsid) bucket, NULL);
- if (flags & OPT_IGNORE_ERROR)
- r = COMPLETE;
- if (r != COMPLETE) {
- ftpfserrno = EPERM;
- return -1;
- }
- if (flush_directory_cache)
- flush_all_directory(bucket);
- return 0;
- }
-
- /* This routine is called as the last step in load_setup */
- void
- ftpfs_init_passwd(void)
- {
- struct passwd *passwd_info;
- char *p, hostname[MAXHOSTNAMELEN];
- struct hostent *hp;
-
- ftpfs_anonymous_passwd = load_anon_passwd ();
- if (ftpfs_anonymous_passwd)
- return;
-
- if ((passwd_info = getpwuid (geteuid ())) == NULL)
- p = "guest";
- else
- p = passwd_info->pw_name;
- gethostname(hostname, sizeof(hostname));
- hp = gethostbyname(hostname);
- if (hp != NULL)
- ftpfs_anonymous_passwd = copy_strings (p, "@", hp->h_name, NULL);
- else
- ftpfs_anonymous_passwd = copy_strings (p, "@", hostname, NULL);
- endpwent ();
- }
-
- void
- ftpfs_init ()
- {
- ftpfs_connections_list = linklist_init();
- ftpfs_directory_timeout = FTPFS_DIRECTORY_TIMEOUT;
- }
-
- void
- ftpfs_done(void)
- {
- linklist_destroy(ftpfs_connections_list, ftpfs_connection_destructor);
- }
-
- /* The callbacks */
-
- struct ftpfs_filp {
- unsigned int has_changed:1;
- struct ftpentry *fe;
- int local_handle;
- };
-
- static void *ftpfs_open (char *file, int flags, int mode)
- {
- struct ftpfs_filp *fp;
- struct ftpentry *fe;
-
- fp = xmalloc(sizeof(struct ftpfs_filp), "struct ftpfs_filp");
- if (fp == NULL) {
- ftpfserrno = ENOMEM;
- return NULL;
- }
- fe = get_file_entry(file, FTPFS_OPEN | FTPFS_RESOLVE_SYMLINK, flags);
- if (fe == NULL) {
- free(fp);
- return NULL;
- }
- if (!isremotecopy) {
- fp->local_handle = open(fe->local_filename, flags, mode);
- if (fp->local_handle < 0) {
- ftpfserrno = errno;
- free(fp);
- return NULL;
- }
- } else
- fp->local_handle = -1;
- #ifdef UPLOAD_ZERO_LENGTH_FILE
- fp->has_changed = fe->freshly_created;
- #else
- fp->has_changed = 0;
- #endif
- fp->fe = fe;
- qlock(fe->bucket)++;
- fe->count++;
- return fp;
- }
-
- static int ftpfs_read (void *data, char *buffer, int count)
- {
- struct ftpfs_filp *fp;
- int n;
-
- fp = data;
- n = read(fp->local_handle, buffer, count);
- if (n < 0)
- ftpfserrno = errno;
- return n;
- }
-
- int ftpfs_write (void *data, char *buf, int nbyte)
- {
- struct ftpfs_filp *fp;
- int n;
-
- fp = data;
- n = write(fp->local_handle, buf, nbyte);
- if (n < 0)
- ftpfserrno = errno;
- fp->has_changed = 1;
- return n;
- }
-
- static int ftpfs_close (void *data)
- {
- struct ftpfs_filp *fp = data;
- int result = 0;
-
- if (fp->has_changed) {
- if (!store_file(fp->fe)) {
- ftpfserrno = EIO;
- result = -1;
- }
- flush_all_directory(fp->fe->bucket);
- }
- if (fp->local_handle >= 0)
- close(fp->local_handle);
- qlock(fp->fe->bucket)--;
- ftpentry_destructor(fp->fe);
- free(fp);
- return result;
- }
-
- static int ftpfs_errno (void)
- {
- return ftpfserrno;
- }
-
-
- /* Explanation:
- * On some operating systems (Slowaris 2 for example)
- * the d_name member is just a char long (Nice trick that break everything,
- * so we need to set up some space for the filename.
- */
- struct ftpfs_dirent {
- struct dirent dent;
- #ifdef NEED_EXTRA_DIRENT_BUFFER
- char extra_buffer [MC_MAXPATHLEN];
- #endif
- struct linklist *pos;
- struct ftpfs_dir *dcache;
- };
-
- char *ftpfs_gethome (char *servername)
- {
- struct ftpfs_connection *bucket;
- char *remote_path;
-
- if (!(remote_path = ftpfs_get_path (&bucket, servername)))
- return NULL;
- free (remote_path);
- return qhome(bucket);
- }
-
- char *ftpfs_getupdir (char *servername)
- {
- struct ftpfs_connection *bucket;
- char *remote_path;
-
- if (!(remote_path = ftpfs_get_path (&bucket, servername)))
- return NULL;
- free (remote_path);
- return qupdir(bucket);
- }
-
- static void *ftpfs_opendir (char *dirname)
- {
- struct ftpfs_connection *bucket;
- char *remote_path;
- struct ftpfs_dirent *dirp;
-
- if (!(remote_path = ftpfs_get_path (&bucket, dirname)))
- return NULL;
- dirp = xmalloc(sizeof(struct ftpfs_dirent), "struct ftpfs_dirent");
- if (dirp == NULL) {
- ftpfserrno = ENOMEM;
- goto error_return;
- }
- dirp->dcache = retrieve_dir(bucket, remote_path);
- if (dirp->dcache == NULL)
- goto error_return;
- dirp->pos = dirp->dcache->file_list->next;
- free(remote_path);
- dirp->dcache->count++;
- return (void *)dirp;
- error_return:
- vfs_add_noncurrent_stamps (&ftpfs_vfs_ops, (vfsid) bucket, NULL);
- free(remote_path);
- free(dirp);
- return NULL;
- }
-
- static void *ftpfs_readdir (void *data)
- {
- struct ftpentry *fe;
- struct ftpfs_dirent *dirp = data;
-
- if (dirp->pos == dirp->dcache->file_list)
- return NULL;
- fe = dirp->pos->data;
- strcpy (&(dirp->dent.d_name [0]), fe->name);
- #ifndef DIRENT_LENGTH_COMPUTED
- dirp->d_namlen = strlen (dirp->d_name);
- #endif
- dirp->pos = dirp->pos->next;
- return (void *) &dirp->dent;
- }
-
- static int ftpfs_closedir (void *info)
- {
- struct ftpfs_dirent *dirp = info;
- ftpfs_dir_destructor(dirp->dcache);
- free(dirp);
- return 0;
- }
-
- static int ftpfs_lstat (char *path, struct stat *buf)
- {
- struct ftpentry *fe;
-
- fe = get_file_entry(path, FTPFS_FREE_RESOURCE, 0);
- if (fe) {
- *buf = fe->s;
- return 0;
- }
- else
- return -1;
- }
-
- static int ftpfs_stat (char *path, struct stat *buf)
- {
- struct ftpentry *fe;
-
- fe = get_file_entry(path, FTPFS_RESOLVE_SYMLINK | FTPFS_FREE_RESOURCE, 0);
- if (fe) {
- if (!S_ISLNK(fe->s.st_mode))
- *buf = fe->s;
- else
- *buf = *fe->l_stat;
- return 0;
- }
- else
- return -1;
- }
-
- int ftpfs_fstat (void *data, struct stat *buf)
- {
- struct ftpfs_filp *fp = data;
-
- if (!S_ISLNK(fp->fe->s.st_mode))
- *buf = fp->fe->s;
- else
- *buf = *fp->fe->l_stat;
- return 0;
- }
-
- int ftpfs_chmod (char *path, int mode)
- {
- char buf[40];
-
- sprintf(buf, "SITE CHMOD %4.4o %%s", mode & 07777);
- return send_ftp_command(path, buf, OPT_IGNORE_ERROR | OPT_FLUSH);
- }
-
- int ftpfs_chown (char *path, int owner, int group)
- {
- #if 0
- ftpfserrno = EPERM;
- return -1;
- #else
- /* Everyone knows it is not possible to chown remotely, so why bother them.
- If someone's root, then copy/move will always try to chown it... */
- return 0;
- #endif
- }
-
- static int ftpfs_readlink (char *path, char *buf, int size)
- {
- struct ftpentry *fe;
-
- fe = get_file_entry(path, FTPFS_FREE_RESOURCE, 0);
- if (!fe)
- return -1;
- if (!S_ISLNK(fe->s.st_mode)) {
- ftpfserrno = EINVAL;
- return -1;
- }
- if (fe->linkname == NULL) {
- ftpfserrno = EACCES;
- return -1;
- }
- if (strlen(fe->linkname) >= size) {
- ftpfserrno = ERANGE;
- return -1;
- }
- strncpy(buf, fe->linkname, size);
- return strlen(fe->linkname);
- }
-
- static int ftpfs_unlink (char *path)
- {
- return send_ftp_command(path, "DELE %s", 1);
- }
-
- static int ftpfs_symlink (char *n1, char *n2)
- {
- ftpfserrno = EPERM;
- return -1;
- }
-
- static int ftpfs_rename (char *path1, char *path2)
- {
- ftpfserrno = EPERM;
- return -1;
- }
-
- static int ftpfs_chdir (char *path)
- {
- char *remote_path;
- struct ftpfs_connection *bucket;
- int r;
-
- retry:
- if (!(remote_path = ftpfs_get_path(&bucket, path)))
- return -1;
- got_sigpipe = 0;
- r = command (bucket, WAIT_REPLY, "CWD %s", remote_path);
- if (got_sigpipe)
- goto retry;
-
- if (r != COMPLETE) {
- ftpfserrno = EIO;
- free(remote_path);
- } else {
- if (qcdir(bucket))
- free(qcdir(bucket));
- qcdir(bucket) = remote_path;
- }
- vfs_add_noncurrent_stamps (&ftpfs_vfs_ops, (vfsid) bucket, NULL);
- return r == COMPLETE ? 0 : -1;
- }
-
- static int ftpfs_lseek (void *data, off_t offset, int whence)
- {
- struct ftpfs_filp *fp = data;
-
- return lseek(fp->local_handle, offset, whence);
- }
-
- static int ftpfs_mknod (char *path, int mode, int dev)
- {
- ftpfserrno = EPERM;
- return -1;
- }
-
- static int ftpfs_mkdir (char *path, mode_t mode)
- {
- return send_ftp_command(path, "MKD %s", 1);
- }
-
- static int ftpfs_rmdir (char *path)
- {
- return send_ftp_command(path, "RMD %s", 1);
- }
-
- static int ftpfs_link (char *p1, char *p2)
- {
- ftpfserrno = EPERM;
- return -1;
- }
-
- static vfsid ftpfs_getid (char *p, struct vfs_stamping **parent)
- {
- struct ftpfs_connection *bucket;
- char *remote_path;
-
- *parent = NULL; /* We are not enclosed in any other fs */
-
- if (!(remote_path = ftpfs_get_path (&bucket, p)))
- return (vfsid) -1;
- else {
- free(remote_path);
- return (vfsid) bucket;
- }
- }
-
- static int ftpfs_nothingisopen (vfsid id)
- {
- return qlock((struct ftpfs_connection *)id) == 0;
- }
-
- static void ftpfs_free (vfsid id)
- {
- struct ftpfs_connection *bucket = (struct ftpfs_connection *) id;
-
- ftpfs_connection_destructor(bucket);
- linklist_delete(ftpfs_connections_list, bucket);
- }
-
- static char *ftpfs_getlocalcopy (char *path)
- {
- struct ftpfs_filp *fp = (struct ftpfs_filp *) ftpfs_open (path, O_RDONLY, 0);
- char *p;
-
- if (fp == NULL)
- return NULL;
- if (fp->fe->local_filename == NULL) {
- ftpfs_close ((void *) fp);
- return NULL;
- }
- p = strdup (fp->fe->local_filename);
- qlock(fp->fe->bucket)++;
- fp->fe->count++;
- ftpfs_close ((void *) fp);
- return p;
- }
-
- static void ftpfs_ungetlocalcopy (char *path, char *local, int has_changed)
- {
- struct ftpfs_filp *fp = (struct ftpfs_filp *) ftpfs_open (path, O_WRONLY, 0);
-
- if (fp == NULL)
- return;
- if (!strcmp (fp->fe->local_filename, local)) {
- fp->has_changed = has_changed;
- qlock(fp->fe->bucket)--;
- ftpentry_destructor(fp->fe);
- ftpfs_close ((void *) fp);
- } else {
- /* Should not happen */
- ftpfs_close ((void *) fp);
- mc_def_ungetlocalcopy (path, local, has_changed);
- }
- }
-
- void ftpfs_set_debug (char *file)
- {
- if ((ftpfs_logfile = fopen (file, "w+")) != NULL)
- ftpfs_debug_server_dialog = 1;
- }
-
- #ifdef HAVE_MMAP
- caddr_t ftpfs_mmap (caddr_t addr, size_t len, int prot, int flags, void *data, off_t offset)
- {
- return (caddr_t)-1; /* We do not mmap to far away */
- }
-
- int ftpfs_munmap (caddr_t addr, size_t len, void *data)
- {
- return -1;
- }
- #endif
-
- vfs ftpfs_vfs_ops = {
- ftpfs_open,
- ftpfs_close,
- ftpfs_read,
- ftpfs_write,
-
- ftpfs_opendir,
- ftpfs_readdir,
- ftpfs_closedir,
-
- ftpfs_stat,
- ftpfs_lstat,
- ftpfs_fstat,
-
- ftpfs_chmod,
- ftpfs_chown,
-
- ftpfs_readlink,
- ftpfs_symlink,
- ftpfs_link,
- ftpfs_unlink,
-
- ftpfs_rename,
- ftpfs_chdir,
- ftpfs_errno,
- ftpfs_lseek,
- ftpfs_mknod,
-
- ftpfs_getid,
- ftpfs_nothingisopen,
- ftpfs_free,
-
- ftpfs_getlocalcopy,
- ftpfs_ungetlocalcopy,
-
- ftpfs_mkdir,
- ftpfs_rmdir,
- ftpfs_ctl,
- ftpfs_setctl
- #ifdef HAVE_MMAP
- , ftpfs_mmap,
- ftpfs_munmap
- #endif
- };
-
- #ifdef USE_NETRC
- static char buffer[100];
- static char *netrc, *netrcp;
-
- static int netrc_next (void)
- {
- char *p;
- int i;
- static char *keywords [] = { "default", "machine",
- "login", "password", "passwd",
- "account", "macdef" };
-
- while (1) {
- netrcp = skip_separators (netrcp);
- if (*netrcp != '\n')
- break;
- netrcp++;
- }
- if (!*netrcp)
- return 0;
- p = buffer;
- if (*netrcp == '"') {
- for (;*netrcp != '"' && *netrcp; netrcp++) {
- if (*netrcp == '\\')
- netrcp++;
- *p++ = *netrcp;
- }
- } else {
- for (;*netrcp != '\n' && *netrcp != '\t' && *netrcp != ' ' &&
- *netrcp != ',' && *netrcp; netrcp++) {
- if (*netrcp == '\\')
- netrcp++;
- *p++ = *netrcp;
- }
- }
- *p = 0;
- if (!*buffer)
- return 0;
- for (i = 0; i < sizeof (keywords) / sizeof (keywords [0]); i++)
- if (!strcmp (keywords [i], buffer))
- break;
- return i + 1;
- }
-
- int lookup_netrc (char *host, char **login, char **pass)
- {
- char *netrcname, *tmp;
- char hostname[MAXHOSTNAMELEN], *domain;
- int c, d, keyword;
- struct stat mystat;
- static int be_angry = 1;
- static struct rupcache {
- struct rupcache *next;
- char *host;
- char *login;
- char *pass;
- } *rup_cache = NULL, *rupp;
-
- for (rupp = rup_cache; rupp != NULL; rupp = rupp->next)
- if (!strcmp (host, rupp->host)) {
- if (rupp->login != NULL)
- *login = strdup (rupp->login);
- if (rupp->pass != NULL)
- *pass = strdup (rupp->pass);
- return 0;
- }
- netrcname = xmalloc (strlen (home_dir) + strlen ("/.netrc") + 1, "netrc");
- strcpy (netrcname, home_dir);
- strcat (netrcname, "/.netrc");
- netrcp = netrc = load_file (netrcname);
- if (netrc == NULL) {
- free (netrcname);
- return 0;
- }
- if (gethostname (hostname, sizeof (hostname)) < 0)
- *hostname = 0;
- if (!(domain = strchr (hostname, '.')))
- domain = "";
-
- while ((keyword = netrc_next ())) {
- if (keyword == 2) {
- if (netrc_next () != 8)
- continue;
- if (strcasecmp (host, buffer) &&
- ((tmp = strchr (host, '.')) == NULL ||
- strcasecmp (tmp, domain) ||
- strncasecmp (host, buffer, tmp - host) ||
- buffer [tmp - host]))
- continue;
- } else if (keyword != 1)
- continue;
- while ((keyword = netrc_next ()) > 2) {
- switch (keyword) {
- case 3:
- if (netrc_next ())
- if (*login == NULL)
- *login = strdup (buffer);
- else if (strcmp (*login, buffer))
- keyword = 20;
- break;
- case 4:
- case 5:
- if (strcmp (*login, "anonymous") && strcmp (*login, "ftp") &&
- stat (netrcname, &mystat) >= 0 &&
- (mystat.st_mode & 077)) {
- if (be_angry) {
- message (1, "Error", "~/.netrc file has not correct mode.\n"
- "Remove password or correct mode.");
- be_angry = 0;
- }
- free (netrc);
- free (netrcname);
- return -1;
- }
- if (netrc_next () && *pass == NULL)
- *pass = strdup (buffer);
- break;
- case 6:
- if (stat (netrcname, &mystat) >= 0 &&
- (mystat.st_mode & 077)) {
- if (be_angry) {
- message (1, "Error", "~/.netrc file has not correct mode.\n"
- "Remove password or correct mode.");
- be_angry = 0;
- }
- free (netrc);
- free (netrcname);
- return -1;
- }
- netrc_next ();
- break;
- case 7:
- for (;;) {
- while (*netrcp != '\n' && *netrcp);
- if (*netrcp != '\n')
- break;
- netrcp++;
- if (*netrcp == '\n' || !*netrcp)
- break;
- }
- break;
- }
- if (keyword == 20)
- break;
- }
- if (keyword == 20)
- continue;
- else
- break;
- }
- rupp = (struct rupcache *) xmalloc (sizeof (struct rupcache), "");
- rupp->host = strdup (host);
- rupp->login = rupp->pass = 0;
-
- if (*login != NULL)
- rupp->login = strdup (*login);
- if (*pass != NULL)
- rupp->pass = strdup (*pass);
- rupp->next = rup_cache;
- rup_cache = rupp;
-
- free (netrc);
- free (netrcname);
- return 0;
- }
-
- #ifndef HAVE_STRNCASECMP
- int strncasecmp (char *s, char *d, int l)
- {
- int result;
-
- while (l--){
- if (result = (0x20 | *s) - (0x20 | *d))
- break;
- if (!*s)
- return 0;
- s++;
- d++;
- }
- }
- #endif
- #endif /* USE_NETRC */
-